# coding:utf-8

from xml.dom import minidom


class SimpleDOMObj:
    """
    Base DOMObj.
    """

    def __init__(self, document: minidom.Document, name):
        self.document = document
        self.name = name
        self.attributes = {}

    def set_attributes(self, key_values):
        for key in key_values:
            self.attributes[key] = key_values[key]

    def to_dom(self):
        node = self.document.createElement(self.name)
        for key in self.attributes:
            node.setAttribute(key, str(self.attributes[key]))
        return node


class Point(SimpleDOMObj):
    """
    Point Model.
    """

    def __init__(self, document, *coords):
        super().__init__(document, 'point')
        self.attributes['x'] = 0
        self.attributes['y'] = 0
        self.attributes['z'] = 0
        if len(coords) >= 1:
            self.attributes['x'] = coords[0]
        if len(coords) >= 2:
            self.attributes['y'] = coords[1]
        if len(coords) >= 3:
            self.attributes['z'] = coords[2]

    def set_coords(self, *coords):
        if len(coords) >= 2:
            self.attributes['x'] = coords[0]
            self.attributes['y'] = coords[1]
        if len(coords) >= 3:
            self.attributes['z'] = coords[2]


class PointSet(SimpleDOMObj):
    """
    PointSet Model.
    """

    def __init__(self, document):
        super().__init__(document, 'pointSet')
        self.point_list = []

    def add_point(self, point: Point):
        self.point_list.append(point)

    def add_points(self, points):
        for point in points:
            self.point_list.append(point)

    def to_dom(self):
        dom = super().to_dom()
        for point in self.point_list:
            dom.appendChild(point.to_dom())
        return dom


class Predecessor(SimpleDOMObj):
    """
    Predecessor Model.
    """

    def __init__(self, document, *uid):
        super().__init__(document, 'predecessor')
        if len(uid) == 1:
            self.attributes['id'] = uid[0]
        else:
            self.attributes['id'] = ''


class Successor(SimpleDOMObj):
    """
    Predecessor Model.
    """

    def __init__(self, document, *uid):
        super().__init__(document, 'successor')
        if len(uid) == 1:
            self.attributes['id'] = uid[0]
        else:
            self.attributes['id'] = ''


class Neighbor(SimpleDOMObj):
    """
    Neighbor Model.
    """

    def __init__(self, document, *uid):
        super().__init__(document, 'neighbor')
        if len(uid) == 1:
            self.attributes['id'] = uid[0]
        else:
            self.attributes['id'] = ''


class Link(SimpleDOMObj):
    """
    Link Model.
    """

    def __init__(self, document):
        super().__init__(document, 'link')
        self.child_list = []

    def add_child(self, child: SimpleDOMObj):
        self.child_list.append(child)

    def add_child_list(self, child_list):
        for child in child_list:
            self.child_list.append(child)

    def to_dom(self):
        dom = super().to_dom()
        for child in self.child_list:
            dom.appendChild(child.to_dom())
        return dom


class Geometry(SimpleDOMObj):
    """
    Geometry Model.
    """

    def __init__(self, document):
        super().__init__(document, 'geometry')
        self.child_list = []

    def add_child(self, child):
        self.child_list.append(child)

    def add_child_list(self, child_list):
        for child in child_list:
            self.child_list.append(child)

    def to_dom(self):
        dom = super().to_dom()
        for child in self.child_list:
            dom.appendChild(child.to_dom())
        return dom


class Lane(SimpleDOMObj):
    """
    Lane Model.
    """

    def __init__(self, document):
        super().__init__(document, 'lane')
        self.attributes[
            'type'] = 'none'  # none, driving, biking, parking, through, onRamp, offRamp, connectingRamp, shoulder
        self.attributes['id'] = -1
        self.attributes['uid'] = ''  # global id
        self.attributes['level'] = 0
        self.attributes['direction'] = 'none'  # forward, backward, bidirection
        self.attributes['turnType'] = 'none'  # noTurn, leftTurn, rightTurn, uTurn
        self.width_list = []
        self.road_mark_list = []
        self.speed = Lane.LaneSpeed(document)
        self.center_line = Lane.CenterLine(document)
        self.border = Lane.Border(document)

    class Width(SimpleDOMObj):
        """
        Width Model.
        """

        def __init__(self, document):
            super().__init__(document, 'width')
            self.attributes = {
                'sOffset': '0',
                'a': '0',
                'b': '0',
                'c': '0',
                'd': '0'
            }

        def set_attributes(self, sOffset, *args):
            self.attributes['sOffset'] = str(sOffset)
            args_length = len(args)
            if args_length >= 1:
                self.attributes['a'] = str(args[0])
            if args_length >= 2:
                self.attributes['b'] = str(args[1])
            if args_length >= 3:
                self.attributes['c'] = str(args[2])
            if args_length >= 4:
                self.attributes['d'] = str(args[3])

    class RoadMark(SimpleDOMObj):
        """
        Road Mark Model.
        """

        def __init__(self, document):
            super().__init__(document, 'roadMark')
            self.attributes = {
                'sOffset': '0',
                'type': 'none',
                'weight': 'standard',
                'color': 'standard',
                'width': '0'
            }

    class Border(SimpleDOMObj):
        """
        Lane Border Model.
        """

        def __init__(self, document):
            super().__init__(document, 'border')
            self.geometry_list = []

        def add_geometry(self, geometry):
            self.geometry_list.append(geometry)

        def add_geometry_list(self, geometry_list):
            for geometry in geometry_list:
                self.geometry_list.append(geometry)

        def to_dom(self):
            dom = super().to_dom()
            for geometry in self.geometry_list:
                dom.appendChild(geometry.to_dom())
            return dom

    class CenterLine(SimpleDOMObj):
        """
        CenterLine Model.
        """

        def __init__(self, document):
            super().__init__(document, 'centerLine')
            self.geometry_list = []

        def add_geometry(self, geometry):
            self.geometry_list.append(geometry)

        def add_geometry_list(self, geometry_list):
            for geometry in geometry_list:
                self.geometry_list.append(geometry)

        def to_dom(self):
            dom = super().to_dom()
            for geometry in self.geometry_list:
                dom.appendChild(geometry.to_dom())
            return dom

    class LaneSpeed(SimpleDOMObj):
        """
        LaneSpeed Model.
        """

        def __init__(self, document):
            super().__init__(document, 'speed')
            self.attributes['min'] = 0
            self.attributes['max'] = 0

    class SampleAssociation(SimpleDOMObj):
        """
        Sample Association.
        """

        def __init__(self, document):
            super().__init__(document, 'sampleAssociate')
            self.attributes['sOffset'] = 0
            self.attributes['leftWidth'] = 0
            self.attributes['rightWidth'] = 0

    class SampleAssociations(SimpleDOMObj):
        """
        Sample Associations.
        """

        def __init__(self, document):
            super().__init__(document, 'sampleAssociates')
            self.attributes['sOffset'] = 0
            self.attributes['leftWidth'] = 0
            self.attributes['rightWidth'] = 0
            self.sample_associates = []

        def add_sample_associate(self, *sample_associates):
            for sample_associate in sample_associates:
                self.sample_associates.append(sample_associate)

        def to_dom(self):
            dom = super().to_dom()
            for sample_associate in self.sample_associates:
                dom.appendChild(sample_associate.to_dom())
            return dom

    def add_width(self, width):
        self.width_list.append(width)

    def add_road_mark(self, road_mark):
        self.road_mark_list.append(road_mark)

    def add_center_line(self, center_line):
        self.center_line = center_line

    def add_border(self, border):
        self.border = border

    def add_speed(self, speed):
        self.speed = speed

    def to_dom(self):
        dom = super().to_dom()
        for width_item in self.width_list:
            width_item_dom = width_item.to_dom()
            dom.appendChild(width_item_dom)
        for road_mark_item in self.road_mark_list:
            road_mark_item_dom = road_mark_item.to_dom()
            dom.appendChild(road_mark_item_dom)
        if isinstance(self.speed, Lane.LaneSpeed):
            dom.appendChild(self.speed.to_dom())
        if isinstance(self.center_line, Lane.CenterLine):
            dom.appendChild(self.center_line.to_dom())
        if isinstance(self.border, Lane.Border):
            dom.appendChild(self.border.to_dom())
        return dom


class LaneSection(SimpleDOMObj):
    """
    LaneSection Model.
    """

    def __init__(self, document):
        super().__init__(document, 'laneSection')
        self.left_lanes = []
        self.center_lanes = []
        self.right_lanes = []

    def to_dom(self):
        dom = super().to_dom()
        left_dom = SimpleDOMObj(self.document, 'left').to_dom()
        center_dom = SimpleDOMObj(self.document, 'center').to_dom()
        right_dom = SimpleDOMObj(self.document, 'right').to_dom()
        for lane in self.left_lanes:
            left_dom.appendChild(lane.to_dom())
        for lane in self.center_lanes:
            center_dom.appendChild(lane.to_dom())
        for lane in self.right_lanes:
            right_dom.appendChild(lane.to_dom())
        dom.appendChild(left_dom)
        dom.appendChild(center_dom)
        dom.appendChild(right_dom)
        return dom


class Lanes(SimpleDOMObj):
    """
    Lanes Model.
    """

    def __init__(self, document):
        super().__init__(document, 'lanes')
        self.lane_section_list = []

    def add_lane_section(self, *lane_section_list: LaneSection):
        """

        :type lane_section_list: LaneSection objects
        """
        for lane_section in lane_section_list:
            self.lane_section_list.append(lane_section)

    def to_dom(self):
        dom = super().to_dom()
        for lane_section in self.lane_section_list:
            dom.appendChild(lane_section.to_dom())
        return dom


class Road(SimpleDOMObj):
    """
    Road Model.
    """

    def __init__(self, document):
        super().__init__(document, 'road')
        self.route_view = Road.RouteView(document)
        self.link = Link(document)
        self.lanes_list = []
        self.road_objects = []
        self.road_signals = []

    class RouteView(SimpleDOMObj):
        """
        Route View Model.
        """

        def __init__(self, document):
            super().__init__(document, 'routeView')
            self.geometry_list = []

        def add_geometry(self, geometry):
            self.geometry_list.append(geometry)

        def add_geometry_list(self, geometry_list):
            for geometry in geometry_list:
                self.geometry_list.append(geometry)

        def to_dom(self):
            dom = super().to_dom()
            for geometry in self.geometry_list:
                dom.appendChild(geometry.to_dom)
            return dom

    def add_lanes(self, *lanes_list: Lanes):
        for lanes in lanes_list:
            self.lanes_list.append(lanes)

    def add_road_objects(self, *road_objects):
        pass

    def add_road_signals(self, *road_signals):
        pass

    def to_dom(self):
        dom = super().to_dom()
        dom.appendChild(self.link.to_dom())
        dom.appendChild(self.route_view.to_dom())
        for lanes in self.lanes_list:
            dom.appendChild(lanes.to_dom())
        for road_object in self.road_objects:
            dom.appendChild(road_object.to_dom())
        for road_signal in self.road_signals:
            dom.appendChild(road_signal.to_dom())
        return dom


class Header(SimpleDOMObj):
    """
    Header Model.
    """

    def __init__(self, document):
        super().__init__(document, 'header')
        self.attributes['geoReference'] = ''


class OpenDRIVE(SimpleDOMObj):
    """
    OpenDRIVE Model.
    """

    def __init__(self, document):
        super().__init__(document, 'OpenDRIVE')
        self.attributes['revMajor'] = 1
        self.attributes['revMinor'] = 0
        self.attributes['name'] = ''
        self.attributes['version'] = 1.
        self.attributes['date'] = ''  # YYYY-MM-DD Thh:mm:ss
        self.attributes['north'] = 0.
        self.attributes['south'] = 0.
        self.attributes['east'] = 0.
        self.attributes['west'] = 0.
        self.attributes['vendor'] = 'DeepMotion'
        self.header = Header(document)
        self.road_list = []
        self.junction_list = []

    def add_roads(self, *roads: Road):
        for road in roads:
            self.road_list.append(road)

    def to_dom(self):
        dom = super().to_dom()
        dom.appendChild(self.header.to_dom())
        for road in self.road_list:
            dom.appendChild(road.to_dom())
        for junction in self.junction_list:
            dom.appendChild(junction.to_dom())
        return dom


if __name__ == "__main__":
    xml = minidom.Document()
    root = OpenDRIVE(xml)
    root.header.attributes['geoReference'] = '<![CDATA[+proj=longlat +ellps=WGS84 +datum=WGS84 +no_defs]]>'
    for i in range(1, 6):
        road = Road(xml)
        road.set_attributes({'id': i})
        road.link.add_child(Predecessor(xml, i - 1))
        road.link.add_child(Successor(xml, i + 1))
        lanes = Lanes(xml)
        lane_section = LaneSection(xml)
        lane_1 = Lane(xml)
        geo_1 = Geometry(xml)
        point_set_1 = PointSet(xml)
        point_set_1.add_points([Point(xml, i, i, i), Point(xml, i + 0.5, i + 0.5, i + 0.5)])
        geo_1.add_child(point_set_1)
        lane_1.center_line.geometry_list.append(point_set_1)
        geo_2 = Geometry(xml)
        point_set_2 = PointSet(xml)
        point_set_2.add_point(Point(xml, i, i, i))
        point_set_2.add_point(Point(xml, i, i + 0.5, i))
        point_set_2.add_point(Point(xml, i + 0.5, i + 0.5, i))
        point_set_2.add_point(Point(xml, i + 0.5, i, i))
        point_set_2.add_point(Point(xml, i, i, i))
        geo_2.add_child(point_set_2)
        border = Lane.Border(xml)
        border.add_geometry(geo_2)
        lane_1.add_border(border)
        lane_section.center_lanes.append(lane_1)
        lanes.add_lane_section(lane_section)
        road.add_lanes(lanes)
        root.add_roads(road)
    xml.appendChild(root.to_dom())
    a = open('output.xml', 'w')
    a.write(xml.toxml())
    a.close()
